/*
 * cr_route.c
 *
 *  Created on: 2010-3-25
 *      Author: root
 */
#include <string.h>
#include"ddc/ddc_router.h"
//#include "lib/prefix.h"
#include "ddc/ddc_spf.h"
#include "ddc/ddc.h"
#include "lib/memory.h"
#include "lib/zebra.h"

void
cr_linkstate_prefix2str (struct prefix *prefix, char *buf, int size)
{
  u_int32_t adv_router, id;
  char adv_router_str[16], id_str[16];
  memcpy (&adv_router, &prefix->u.prefix6.s6_addr[0], 4);
  memcpy (&id, &prefix->u.prefix6.s6_addr[4], 4);
  inet_ntop (AF_INET, &adv_router, adv_router_str, sizeof (adv_router_str));
  inet_ntop (AF_INET, &id, id_str, sizeof (id_str));
//  if (ntohl (id))
//    snprintf (buf, size, "%s Net-ID: %s", adv_router_str, id_str);
//  else
//    snprintf (buf, size, "%s", adv_router_str);
}

//copy from ospf6d.c. add by yangyu
struct route_node *route_prev (struct route_node *node)
{
  struct route_node *end;
  struct route_node *prev = NULL;

  end = node;
  node = node->parent;
  if (node)
    route_lock_node (node);
  while (node)
    {
      prev = node;
      node = route_next (node);
      if (node == end)
        {
          route_unlock_node (node);
          node = NULL;
        }
    }
  route_unlock_node (end);
  if (prev)
    route_lock_node (prev);

  return prev;
}

void cr_linkstate_prefix (u_int32_t adv_router, u_int32_t id,struct prefix *prefix)
{
  memset (prefix, 0, sizeof (struct prefix));
  prefix->family = AF_INET6;
  prefix->prefixlen = 64;
  memcpy (&prefix->u.prefix6.s6_addr[0], &adv_router, 4);
  memcpy (&prefix->u.prefix6.s6_addr[4], &id, 4);
}
void cr_route_lock (struct cr_route *route)
{
  route->lock++;
}
void cr_route_unlock (struct cr_route *route)
{
  route->lock--;
  if (route->lock == 0)
    cr_route_delete (route);
}
void cr_route_delete (struct cr_route *route)
{
  XFREE (MTYPE_OSPF6_ROUTE, route);
}

struct cr_route *cr_route_create ()
{
	  struct cr_route *route;
	  route = XCALLOC (MTYPE_OSPF6_ROUTE, sizeof (struct cr_route));
	  return route;
}

struct cr_route * cr_route_lookup (struct prefix *prefix,struct cr_route_table *table)
{
	struct route_node *node;
	struct cr_route *route;

	node = route_node_lookup(table->table, prefix);
	if (node == NULL)
		return NULL;

	route = (struct cr_route *) node->info;
	return route;
}

static int
cr_route_cmp (struct cr_route *ra, struct cr_route *rb)
{
  if (ra->type != rb->type)
    return (ra->type - rb->type);

  if (ra->path.area_id != rb->path.area_id)
    return (ntohl (ra->path.area_id) - ntohl (rb->path.area_id));

  if (ra->path.type != rb->path.type)
    return (ra->path.type - rb->path.type);

  if (ra->path.type == OSPF6_PATH_TYPE_EXTERNAL2)
    {
      if (ra->path.cost_e2 != rb->path.cost_e2)
        return (ra->path.cost_e2 - rb->path.cost_e2);
    }
  else
    {
      if (ra->path.cost != rb->path.cost)
        return (ra->path.cost - rb->path.cost);
    }

  return 0;
}


struct cr_route * cr_route_add (struct cr_route *route, struct cr_route_table *table)
{
	 struct route_node *node, *nextnode, *prevnode;
	 struct cr_route *current = NULL;
	 struct cr_route *prev = NULL, *old = NULL, *next = NULL;
	 char buf[64];
	 struct timeval now;

	  if (route->type == OSPF6_DEST_TYPE_LINKSTATE)
	    cr_linkstate_prefix2str (&route->prefix, buf, sizeof (buf));
	  else
	    prefix2str (&route->prefix, buf, sizeof (buf));

	 gettimeofday (&now, NULL);

	 node = route_node_get (table->table, &route->prefix);
	 route->rnode = node;

	    /* find place to insert */
	 for (current = node->info; current; current = current->next)
	    {
	      if (! cr_route_is_same (current, route))
	        next = current;
	      else if (current->type != route->type)
	        prev = current;
	      else if (cr_route_is_same_origin (current, route))
	        old = current;
	      else if (cr_route_cmp (current, route) > 0)
	        next = current;
	      else
	        prev = current;

	      if (old || next)
	        break;
	    }

	  if (old)
	    {
	      /* if route does not actually change, return unchanged */
	      if (cr_route_is_identical (old, route))
	        {
//	          if (IS_OSPF6_DEBUG_ROUTE (TABLE))
//	            zlog_info ("  identical route found, ignore");

	          cr_route_delete (route);
	          SET_FLAG (old->flag, OSPF6_ROUTE_ADD);
	          return old;
	        }

	      /* replace old one if exists */
	      if (node->info == old)
	        {
	          node->info = route;
	          SET_FLAG (route->flag, OSPF6_ROUTE_BEST);
	        }

	      if (old->prev)
	        old->prev->next = route;
	      route->prev = old->prev;
	      if (old->next)
	        old->next->prev = route;
	      route->next = old->next;

	      route->installed = old->installed;
	      route->changed = now;

	      cr_route_unlock (old); /* will be deleted later */
	      cr_route_lock (route);

	      SET_FLAG (route->flag, OSPF6_ROUTE_CHANGE);


	      if (table->hook_add)
	        (*table->hook_add) (route);

	      return route;
	    }
	  /* insert if previous or next node found */
	   if (prev || next)
	     {
	       if (prev == NULL)
	         prev = next->prev;
	       if (next == NULL)
	         next = prev->next;

	       if (prev)
	         prev->next = route;
	       route->prev = prev;
	       if (next)
	         next->prev = route;
	       route->next = next;

	       if (node->info == next)
	         {

	           node->info = route;
	           UNSET_FLAG (next->flag, OSPF6_ROUTE_BEST);
	           SET_FLAG (route->flag, OSPF6_ROUTE_BEST);
	         }

	       route->installed = now;
	       route->changed = now;

	       cr_route_lock (route);
	       table->count++;


	       SET_FLAG (route->flag, OSPF6_ROUTE_ADD);
	       if (table->hook_add)
	         (*table->hook_add) (route);

	       return route;
	     }
	   /* Else, this is the brand new route regarding to the prefix */

	    node->info = route;
	    SET_FLAG (route->flag, OSPF6_ROUTE_BEST);
	    cr_route_lock (route);
	    route->installed = now;
	    route->changed = now;

	    /* lookup real existing next route */
	    nextnode = node;
	    route_lock_node (nextnode);
	    do {
	      nextnode = route_next (nextnode);
	    } while (nextnode && nextnode->info == NULL);

	    /* set next link */
	    if (nextnode == NULL)
	      route->next = NULL;
	    else
	      {
	        route_unlock_node (nextnode);

	        next = nextnode->info;
	        route->next = next;
	        next->prev = route;
	      }
	    /* lookup real existing prev route */
	     prevnode = node;
	     route_lock_node (prevnode);
	     do {
	       prevnode = route_prev (prevnode);
	     } while (prevnode && prevnode->info == NULL);

	     /* set prev link */
	     if (prevnode == NULL)
	       route->prev = NULL;
	     else
	       {
	         route_unlock_node (prevnode);

	         prev = prevnode->info;
	         while (prev->next && cr_route_is_same (prev, prev->next))
	           prev = prev->next;
	         route->prev = prev;
	         prev->next = route;
	       }

	     table->count++;


	     SET_FLAG (route->flag, OSPF6_ROUTE_ADD);
	     if (table->hook_add)
	       (*table->hook_add) (route);

	     return route;
}
struct cr_route *cr_route_head (struct cr_route_table *table)
{
	struct route_node *node;
	struct cr_route *route;

	node = route_top (table->table);
	  if (node == NULL)
	    return NULL;

	  /* skip to the real existing entry */
	  while (node && node->info == NULL)
	    node = route_next (node);
	  if (node == NULL)
	    return NULL;

	  route_unlock_node (node);
	  assert (node->info);

	  route = (struct cr_route *) node->info;
	  assert (route->prev == NULL);
	  cr_route_lock (route);
	  return route;
}
struct cr_route *cr_route_next (struct cr_route *route)
{
  struct cr_route *next = route->next;

  cr_route_unlock (route);
  if (next)
    cr_route_lock (next);
  return next;
}
